package com.example.service.impl;

import com.example.entity.dto.WorkflowAuditDto;
import com.example.entity.dto.WorkflowReportDto;
import com.example.entity.dto.WorkflowSelectDto;
import com.example.entity.hz.*;
import com.example.entity.report.reportReflection.Audit;
import com.example.entity.report.reportReflection.Report;
import com.example.entity.vo.WorkflowDocumentHisVo;
import com.example.entity.vo.WorkflowDocumentVo;
import com.example.exception.CustomException;
import com.example.mapper.WorkflowdocumentMapper;
import com.example.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.untils.SecurityUtils;
import com.example.untils.StringUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Lan
 * @since 2025-01-03
 */
@Service
@Slf4j
public class WorkflowdocumentServiceImpl extends ServiceImpl<WorkflowdocumentMapper, Workflowdocument> implements WorkflowdocumentService {

    @Autowired
    private WorkflowdocumenthisService hisService;

    @Autowired
    private WorkflowdocumentlevelconfigService dLevelConfigService;

    @Autowired
    private WorkflowdocumentpersonconfigService dPersonConfigService;

    @Autowired
    private WorkflowconfigService configService;

    @Autowired
    private WorkflowlevelconfigServiceImpl levelService;

    @Autowired
    private WorkflowpersonconfigServiceImpl personService;

    @Override
    @Transactional
    public void report(WorkflowReportDto dto) {
        String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //存储具体业务表
        if (StringUtils.isEmpty(dto.getReporterNo()) || StringUtils.isEmpty(dto.getWorkflowConfigNo())
                || StringUtils.isEmpty(dto.getDocumentNo())) {
            throw new CustomException("500", "审批编号、业务编号、上报人编号不能为空");
        }
        //对应的配置单头
        Workflowconfig config = configService.lambdaQuery().eq(Workflowconfig::getWorkflowConfigNo, dto.getWorkflowConfigNo())
                .one();
        if (config == null) throw new CustomException("500", "无效的审批编号");
        //判断业务是否已经在审批
        if (baseMapper.getByConfigAndDocument(dto.getWorkflowConfigNo(),dto.getDocumentNo())!=null) {
            throw new CustomException("500", "当前审批已经上报过");
        }
        Workflowdocument document = new Workflowdocument();
        BeanUtils.copyProperties(dto, document);
        String documentId = UUID.randomUUID().toString();
        document.setWorkflowDocumentID(documentId)
                .setStatus("N").setCreator(SecurityUtils.getLoginUser().getUsername())
                .setCreatorName(SecurityUtils.getLoginUser().getName())
                .setCreateTime(nowTime)
                .setLevel("1").setLastLevel(config.getLastLevel())
                .setWorkflowConfigName(config.getWorkflowConfigName());
        this.save(document);
        //存储业务历史记录表
        Workflowdocumenthis documentHis = new Workflowdocumenthis()
                .setWorkflowDocumentID(documentId)
                .setWorkflowLevel("0").setApproverNo(dto.getReporterNo())
                .setApproverName(dto.getReporterName()).setDocumentNo(dto.getDocumentNo())
                .setApprovalResult("S").setApprovalTime(document.getCreateTime())
                .setStatus("N");
        hisService.save(documentHis);
        //配置的id
        String configId = config.getId();
        //存储审批阶段拷贝
        AtomicReference<String> firstId =new AtomicReference<>();//第一阶段的levelId
        List<String> allLevelConfigId = new ArrayList<>();
        List<Workflowdocumentlevelconfig> dLevelConfig = levelService.lambdaQuery().eq(Workflowlevelconfig::getWorkflowConfigID, configId).list()
                .stream().map(item -> {
                    if (item.getLevel().equals("1")) firstId.set(item.getId());
                    allLevelConfigId.add(item.getId());
                    return new Workflowdocumentlevelconfig()
                            .setWorkflowDocumentID(documentId)
                            .setLevel(item.getLevel()).setWorkflowConfigNo(dto.getWorkflowConfigNo())
                            .setWorkflowLevelConfigID(item.getId()).setWorkflowConfigID(configId)
                            .setWorkflowConfigName(dto.getWorkflowConfigName())
                            .setEndTrigger(item.getEndTrigger())
                            .setRemark(item.getRemark());
                })
                .sorted(Comparator.comparing(Workflowdocumentlevelconfig::getLevel))
                .collect(Collectors.toList());
        dLevelConfigService.saveBatch(dLevelConfig);
        //存储审批人员拷贝
        List<Map<String,String>> persons=new ArrayList<>(); //第一阶段的所有审核人员
        List<Workflowdocumentpersonconfig> dPersonConfig = personService.lambdaQuery()
                .eq(Workflowpersonconfig::getWorkflowConfigID, config.getId())
                .in(Workflowpersonconfig::getWorkflowLevelConfigID, allLevelConfigId).list()
                .stream().map(item -> {
                    if (item.getWorkflowLevelConfigID().equals(firstId.get())){
                        Map<String, String> map = new HashMap<>();
                        map.put("approverNo", item.getApproverNo());
                        map.put("approverName", item.getApproverName());
                        persons.add(map);
                    }
                    Workflowdocumentpersonconfig dp = new Workflowdocumentpersonconfig();
                    BeanUtils.copyProperties(item, dp);
                    return dp.setWorkflowDocumentID(documentId);
                }).collect(Collectors.toList());
        dPersonConfigService.saveBatch(dPersonConfig);
        //触发上报后触发方法
        Workflowconfig one = configService.lambdaQuery()
                .eq(Workflowconfig::getWorkflowConfigNo, document.getWorkflowConfigNo()).one();
        invokeReflection(one.getReportMethod(), 0, document, null, persons, null);
    }

    @Override
    @Transactional
    public void unReport(String workflowConfigNo, String documentNo) {
        String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Workflowdocument document = baseMapper.getByConfigAndDocument(workflowConfigNo,documentNo);
        if (document == null) throw new CustomException("500", "不存在的审批");
        //单据当前状态
        String nowStatus = document.getStatus();
        document.setStatus("R");
        this.updateById(document);
        Workflowdocumenthis documentHis = new Workflowdocumenthis().setWorkflowDocumentID(document.getWorkflowDocumentID())
                .setWorkflowLevel(document.getLevel()).setApproverNo(document.getReporterNo())
                .setApproverName(document.getReporterName()).setDocumentNo(document.getDocumentNo())
                .setApprovalResult("R").setApprovalTime(nowTime)
                .setStatus("Y");
        hisService.save(documentHis);
        //触发取消上报方法
        Workflowconfig one = configService.lambdaQuery()
                .eq(Workflowconfig::getWorkflowConfigNo, document.getWorkflowConfigNo()).one();
        // 通知所有当前阶段的审批人，这个审核被取消报工了，不需要再审了
        List<Map<String, String>> persons = new ArrayList<>();
        //只有审核未完成时才需要通知，审核结束后不需要通知
        if(nowStatus.equals("N"))
            baseMapper.getNowUser(document.getWorkflowDocumentID(),document.getLevel()).forEach(item->{
                Map<String, String> map = new HashMap<>();
                map.put("approverNo", item.getApproverNo());
                map.put("approverName", item.getApproverName());
                persons.add(map);
            });
        invokeReflection(one.getUnReportMethod(), 1, document, null, persons, null);
    }

    @Override
    @Transactional
    public void audit(WorkflowAuditDto dto) {
        String nowTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Workflowdocument document = this.getById(dto.getWorkflowDocumentID());
        if (document == null) throw new CustomException("500", "不存在的审批");
        else if (!document.getStatus().equals("N")) throw new CustomException("500", "审批已结束");
        boolean isCom = false;
        String level = document.getLevel();
        if (dto.getResult().equals("N"))
            // 拒绝
            this.updateById(document.setStatus("V"));
        else if (document.getLevel().equals(document.getLastLevel())){
            // 同意，当前是最后一级,审核通过
            isCom = true;
            //修改状态为结束
            this.lambdaUpdate().eq(Workflowdocument::getWorkflowDocumentID, document.getWorkflowDocumentID())
                    .set(Workflowdocument::getStatus,"Y").update();
        }
        else{
            //同意，审批前往下一阶段
            level = String.valueOf(Integer.parseInt(document.getLevel()) + 1);
            this.lambdaUpdate().eq(Workflowdocument::getWorkflowDocumentID, document.getWorkflowDocumentID())
                    .set(Workflowdocument::getLevel,level).update();
        }

        Workflowdocumenthis his = new Workflowdocumenthis();
        BeanUtils.copyProperties(dto, his);
        his.setWorkflowDocumentID(dto.getWorkflowDocumentID()).setWorkflowLevel(document.getLevel())
                .setDocumentNo(document.getDocumentNo()).setApprovalResult(dto.getResult())
                .setApprovalTime(nowTime)
                .setStatus(dto.getResult().equals("N") ? "Y" : "N");
        hisService.save(his);
        //判断是否已经结束
        if (isCom) {
            //审批结束触发方法
            List<Workflowdocumentlevelconfig> list = dLevelConfigService.lambdaQuery()
                    .eq(Workflowdocumentlevelconfig::getWorkflowDocumentID, dto.getWorkflowDocumentID())
                    .eq(Workflowdocumentlevelconfig::getLevel, level).list();
            if (level.isEmpty()) return;
            Workflowdocumentlevelconfig nowCopy = list.get(0);
            //调用预设方法, 通知上报人
            List<Map<String, String>> persons = new ArrayList<>();
            Map<String, String> map = new HashMap<>();
            map.put("approverNo", document.getReporterNo());
            map.put("approverName", document.getReporterName());
            persons.add(map);
            invokeReflection(nowCopy.getEndTrigger(), 2, document, his, persons, "Y");
        } else {
            //一般审批结果触发方法
            //当前级对应的阶段拷贝（需要当前级的方法）
            Workflowdocumentlevelconfig nowCopy = new Workflowdocumentlevelconfig();
            //下一级对应的阶段拷贝（需要下一级的人员)
            Workflowdocumentlevelconfig nextCopy = new Workflowdocumentlevelconfig();
            List<Workflowdocumentlevelconfig> list = dLevelConfigService.lambdaQuery()
                    .eq(Workflowdocumentlevelconfig::getWorkflowDocumentID, dto.getWorkflowDocumentID())
                    .in(Workflowdocumentlevelconfig::getLevel,
                            new ArrayList<>(Arrays.asList(document.getLevel(), level))).list();
            if (list.isEmpty()) return;
            for (Workflowdocumentlevelconfig conf : list) {
                if (conf.getLevel().equals(document.getLevel()))
                    nowCopy = conf;
                else
                    nextCopy = conf;
            }
            List<Map<String, String>> persons = new ArrayList<>();
            //判断审核是否通过，通过就通知下一级的人，没通过就通知上报人
            if (dto.getResult().equals("N")){
                Map<String, String> map = new HashMap<>();
                map.put("approverNo", document.getReporterNo());
                map.put("approverName", document.getReporterName());
                persons.add(map);
            }else {
                dPersonConfigService.lambdaQuery().eq(Workflowdocumentpersonconfig::getWorkflowDocumentID, dto.getWorkflowDocumentID())
                        .eq(Workflowdocumentpersonconfig::getWorkflowLevelConfigID, nextCopy.getWorkflowLevelConfigID()).list()
                        .forEach(item -> {
                            Map<String, String> map = new HashMap<>();
                            map.put("approverNo", item.getApproverNo());
                            map.put("approverName", item.getApproverName());
                            persons.add(map);
                        });
            }
            //调用预设方法
            invokeReflection(nowCopy.getEndTrigger(), 2, document, his, persons, "N");
        }
    }

    @Override
    public WorkflowDocumentHisVo.WorkflowDocumentHisResult reviewed(WorkflowSelectDto dto) {
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        PageInfo<WorkflowDocumentHisVo> pageInfo = new PageInfo<>(baseMapper.selectHis(dto.getId()));
        return new WorkflowDocumentHisVo.WorkflowDocumentHisResult().setResult(pageInfo.getList())
                .setTotal(pageInfo.getTotal());
    }

    @Override
    public WorkflowDocumentVo.WorkflowDocumentResult underReview(WorkflowSelectDto dto) {
        //TODO 需要考虑阶段问题
        PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
        PageInfo<WorkflowDocumentVo> pageInfo = new PageInfo<>(baseMapper.selectUnderReview(dto.getId()));
        return new WorkflowDocumentVo.WorkflowDocumentResult().setResult(pageInfo.getList())
                .setTotal(pageInfo.getTotal());
    }

    /**
     * 反射方法调用
     *
     * @param method      要反射的方法和方法所在的class文件路径
     * @param type        要调用的方法类型  0-上报后触发的方法  1-取消上报触发的方法  2-审批触发的方法
     * @param document    业务表信息
     * @param documentHis 该次审批的结果信息
     * @param members     调用 审批触发的方法 时 下一级对应的人员信息
     * @param lastLe      最后一级标识 N-不是  Y-是
     */
    private void invokeReflection(String method, Integer type, Workflowdocument document,
                                  Workflowdocumenthis documentHis, List<Map<String, String>> members, String lastLe) {
        if (StringUtils.isEmpty(method)) {
            log.info(document.getWorkflowDocumentID() + documentHis.getWorkflowLevel() + documentHis.getDocumentNo() +
                    "没有设置方法");
            throw new CustomException("500","审核调用的方法异常");
        }
        //申请时间
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Audit audit = null;
        Report report = null;
        if (type == 2) {
            audit = new Audit();
            BeanUtils.copyProperties(documentHis, audit);
            audit.setIsLast(lastLe).setNextMembers(members)
                    .setWorkflowConfigNo(document.getWorkflowConfigNo())
                    .setWorkflowConfigName(document.getWorkflowConfigName())
                    .setDocumentNo(document.getDocumentNo())
                    .setReporterNo(document.getReporterNo())
                    .setReporterName(document.getReporterName());
        } else {
            report = new Report();
            BeanUtils.copyProperties(document, report);
            report.setTime(createTime).setMembers(members);
            if (type == 1) report.setRemark(null);
        }
        reflection(method, type, audit,report);
    }

    /**
     * 反射方法
     *
     * @param method  要反射的方法和方法所在的class文件路径
     * @param type    要调用的方法类型  0-上报后触发的方法  1-取消上报触发的方法  2-审批触发的方法
     * @param audit 审批触发方法传参
     * @param report   上报和取消上报方法传参
     */
    private void reflection(String method, Integer type, Audit audit, Report report) {
        String[] s = method.split(" ");
        //com.xx.xx.xx  class路径（包含文件名）
        //s[1] 方法名
        String className = s[0];
        Class<?> aClass = null;
        try {
            aClass = Class.forName(className);
            Object o = aClass.getDeclaredConstructor().newInstance();
            if (type == 2)
                aClass.getMethod(s[1], Audit.class).invoke(o, audit);
            else
                aClass.getMethod(s[1], Report.class).invoke(o, report);
        } catch (ClassNotFoundException e) {
            throw new CustomException("500", "不存在的类");
        } catch (NoSuchMethodException e) {
            throw new CustomException("500", "方法不存在");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
